package pve

import (
	"fmt"
	"time"

	"gddgame.cc/galaxy/common/service/lobby"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/serialize/msgpack"
)

type pve struct {
	def.Serialize
}

// 按人数，难度，地图分割
type pveComputer struct {
	buckets map[int][]*bucket
}
type bucket struct {
	people  int
	number  int
	clients []*lobby.Client
	time    *time.Time
}

func (computer *pveComputer) init() {
	computer.buckets = make(map[int][]*bucket)
}

func (computer *pveComputer) Refresh(q *lobby.Queue) ([]*lobby.Client, map[string]interface{}) {
	fmt.Println("refresh")
	computer.init()
	index := 0
	for e := q.Clients.Front(); e != nil; e = e.Next() {
		clients, c := computer.Add(e.Value.(*lobby.Client), index)
		if clients != nil {
			return clients, c
		}
		index++
	}
	return nil, nil
}

func (computer *pveComputer) Timeout() ([]*lobby.Client, map[string]interface{}) {
	t := time.Now()
	for people, buckets := range computer.buckets {
		for index, bucket := range buckets {
			//fmt.Println(bucket.time, t, bucket.time.Before(t))
			if bucket.time != nil && bucket.time.Before(t) && bucket.number > 1 {
				options := bucket.clients[0].Options
				options["people"] = bucket.people
				if len(buckets) == 1 {
					buckets = buckets[:0]
				} else if index == 0 {
					buckets = buckets[1:]
				} else if index == len(buckets)-1 {
					buckets = buckets[0:index]
				} else {
					buckets = append(buckets[:index], buckets[index+1:]...)
				}
				computer.buckets[people] = buckets
				return bucket.clients, options
			}
		}
	}
	return nil, nil
}

func (computer *pveComputer) Add(client *lobby.Client, index int) ([]*lobby.Client, map[string]interface{}) {
	//fmt.Printf("%#v\n", client.Users)
	people := client.People
	allPeople := utils.ToInt(client.Options["people"])

	// 如果人数已满足要求
	if people == allPeople {
		//fmt.Printf("add 1:%s,%s", people, allPeople)
		return []*lobby.Client{client}, client.Options
	}
	//todo 目前按匹配一致，日后调整同时匹配多个队列
	buckets, ok := computer.buckets[allPeople]
	if !ok {
		buckets = make([]*bucket, 0, 3)
		computer.buckets[allPeople] = buckets
	}

	var tt *time.Time
	if value, ok := client.Options["seconds"]; ok {
		t := client.Time.Add(time.Duration(utils.ToInt(value)) * time.Second)
		tt = &t
	}
	flag := false
	for _, bucket := range buckets {
		//fmt.Println(bucket.people, people, allPeople)
		if bucket.people+people > allPeople {
			continue
		}
		if bucket.people+people == allPeople {
			//fmt.Println("add 2")
			return append(bucket.clients, client), client.Options
		}
		flag = true
		bucket.people += people
		bucket.number += 1
		bucket.clients = append(bucket.clients, client)
		// 更新最小时间差
		if bucket.time == nil {
			bucket.time = tt
		} else if tt != nil && bucket.time.After(*tt) {
			bucket.time = tt
		}
	}
	if !flag {
		bucket := &bucket{
			people:  people,
			number:  1,
			clients: []*lobby.Client{client},
			time:    tt,
		}

		computer.buckets[allPeople] = append(buckets, bucket)
	}
	return nil, nil
}

var (
	Default *pve
)

func init() {
	Default = &pve{
		Serialize: msgpack.Msgpack(true),
	}
}

func (p *pve) ID() string {
	return "pve"
}
func (p *pve) GetComputer(channel string) lobby.Computer {
	pc := &pveComputer{}
	return pc
}
func (p *pve) Channel(users map[string]string, options map[string]interface{}) (string, []byte, error) {
	mapId := options["mapId"]
	if mapId == nil {
		return "", nil, fmt.Errorf("Empty options:mapId")
	}

	difficulty := options["difficulty"]
	if difficulty == nil {
		return "", nil, fmt.Errorf("Empty options:difficulty")
	}

	people := options["people"]
	if people == nil {
		return "", nil, fmt.Errorf("Empty options:people")
	}
	if utils.ToInt(people) == 0 {
		return "", nil, fmt.Errorf("Empty options:people")
	}

	channel := mapId.(string)
	payload, err := p.Serialize.Marshal(options)
	return channel, payload, err
}

func (p *pve) Parse(channel string, payload []byte) map[string]interface{} {
	data := make(map[string]interface{})
	_ = p.Serialize.Unmarshal(payload, &data)
	return data
}

func (p *pve) Need(users map[string]string, options map[string]interface{}) bool {
	people := options["people"]
	if people == nil {
		return true
	}

	if utils.ToInt(people) != len(users) {
		return true
	} else {
		return false
	}
}
